import * as THREE from 'three'
import { matmul2 } from './data'

const views = [
  {
    left: 0.8,
    bottom: 0.6,
    width: 0.2,
    height: 0.4,
    background: new THREE.Color(0.1, 0.1, 0.2),
    eye: [0, 300, 1800],
    up: [0, 1, 0],
    fov: 30,
    resetCamera: function (camera) {
      camera.position.x = 0
      camera.position.z = 0
      camera.position.y = 0

      camera.rotation.x = 0
      camera.rotation.y = 0
      camera.rotation.z = -Math.PI / 2
    },
    zoom_ratio: 1,
  },
  {
    left: 0.8,
    bottom: 0.3,
    width: 0.2,
    height: 0.3,
    background: new THREE.Color(0.1, 0.2, 0.1),
    eye: [0, 1800, 0],
    up: [0, 0, 1],
    fov: 45,
    resetCamera: function (camera) {
      camera.position.x = 0
      camera.position.z = 0
      camera.position.y = 0

      camera.rotation.x = Math.PI / 2
      camera.rotation.y = 0
      camera.rotation.z = 0
    },
    zoom_ratio: 1,
  },
  {
    left: 0.8,
    bottom: 0.0,
    width: 0.2,
    height: 0.3,
    background: new THREE.Color(0.2, 0.1, 0.1),
    eye: [1400, 800, 1400],
    up: [0, 1, 0],
    fov: 60,
    resetCamera: function (camera) {
      camera.position.x = 0
      camera.position.z = 0
      camera.position.y = 0
      camera.up.set(0, 0, 1)
      camera.lookAt(-3, 0, 0)

      camera.rotation.x = Math.PI / 2
      camera.rotation.y = Math.PI / 2
      camera.rotation.z = 0
    },
    zoom_ratio: 1,
  },
]

const view_handle_dimension = {
  z: {
    x: 0,
    y: 0,
  },
  y: {
    x: 0,
    y: 0,
  },
  x: {
    x: 0,
    y: 0,
  },
}

const view_center = {
  z: {
    x: 0,
    y: 0,
  },
  y: {
    x: 0,
    y: 0,
  },
  x: {
    x: 0,
    y: 0,
  },
}

const view_port_pos = {
  z: {
    x: 0,
    y: 0,
  },
  y: {
    x: 0,
    y: 0,
  },
  x: {
    x: 0,
    y: 0,
  },
}

let selected_box = null
const mouse_start_pos = {
  z: null,
  y: null,
  x: null,
}

class SideViews {
  constructor(mouseEventHandlers) {
    this.on_wheel = mouseEventHandlers.divOnWhell
  }

  init() {
    const arr = ['z', 'y', 'x']

    let rect = this.renderer.domElement.getBoundingClientRect()

    arr.forEach((viewPrefix) => {
      const div = document.getElementById(viewPrefix + '-view-manipulator')
      div.onwheel = (event) => {
        if (this.on_wheel) {
          this.on_wheel(viewPrefix, event.deltaY)
        }
      }

      div.onmouseenter = function () {
        div.focus()
      }

      div.onmouseleave = function () {
        div.blur()
      }

      const lines = getLines(viewPrefix + '-')
      const handles = getHandles(viewPrefix + '-')

      install_edge_hanler(handles.left, lines, { x: -1, y: 0 }, viewPrefix)
      install_edge_hanler(handles.right, lines, { x: 1, y: 0 }, viewPrefix)
      install_edge_hanler(handles.top, lines, { x: 0, y: 1 }, viewPrefix)
      install_edge_hanler(handles.bottom, lines, { x: 0, y: -1 }, viewPrefix)
      install_edge_hanler(handles.topleft, lines, { x: -1, y: 1 }, viewPrefix)
      install_edge_hanler(handles.topright, lines, { x: 1, y: 1 }, viewPrefix)
      install_edge_hanler(handles.bottomleft, lines, { x: -1, y: -1 }, viewPrefix)
      install_edge_hanler(handles.bottomright, lines, { x: 1, y: -1 }, viewPrefix)
      install_edge_hanler(handles.move, lines, null, viewPrefix)

      install_direction_handler('line-direction', lines, viewPrefix, rect)
    })
  }

  update_view_handle(selectedBox) {
    selected_box = selectedBox
    update_view_handle(
      'z',
      views[0].viewport,
      {
        x: selectedBox.scale.y,
        y: selectedBox.scale.x,
      },
      () => {
        onDirectionChanged('z')
      }
    )
    update_view_handle(
      'y',
      views[1].viewport,
      {
        x: selectedBox.scale.x,
        y: selectedBox.scale.z,
      },
      () => {
        onDirectionChanged('y')
      }
    )
    update_view_handle(
      'x',
      views[2].viewport,
      {
        x: selectedBox.scale.y,
        y: selectedBox.scale.z,
      },
      () => {
        onDirectionChanged('x')
      }
    )
  }
}

function install_edge_hanler(handle, lines, direction, viewPrefix) {
  function hide() {
    hide_lines(lines)
  }
  function highlight() {
    highlight_lines(lines)
  }

  handle.onmouseenter = highlight
  handle.onmouseleave = hide

  handle.onmouseup = function () {
    hide()
    handle.onmouseleave = hide
  }

  handle.onmousedown = function (event) {
    highlight()
    disable_handle_except(handle, viewPrefix)

    handle.onmouseleave = null

    mouse_start_pos[viewPrefix] = { x: event.clientX, y: event.clientY }
    var mouse_cur_pos = { x: mouse_start_pos[viewPrefix].x, y: mouse_start_pos[viewPrefix].y }

    const svg = document.getElementById(viewPrefix + '-view-svg')

    svg.onmouseup = function (event) {
      svg.onmousemove = null
      svg.onmouseup = null
      enable_handles(viewPrefix)

      hide()
      handle.onmouseleave = hide

      var handle_delta = {
        x: mouse_cur_pos.x - mouse_start_pos[viewPrefix].x,

        // reverse since it'll be used by 3d-coord system
        y: -(mouse_cur_pos.y - mouse_start_pos[viewPrefix].y),
      }

      var ratio_delta = {
        x: handle_delta.x / view_handle_dimension[viewPrefix].x,
        y: handle_delta.y / view_handle_dimension[viewPrefix].y,
      }

      if (direction) {
        on_edge_changed_axis(ratio_delta, direction, viewPrefix)

        if (event.ctrlKey) {
          on_auto_shrink(direction)
        }
      } else {
        on_moved_axis(viewPrefix, ratio_delta)
      }
    }

    svg.onmousemove = function (event) {
      mouse_cur_pos = { x: event.clientX, y: event.clientY }

      var handle_delta = {
        x: mouse_cur_pos.x - mouse_start_pos[viewPrefix].x,
        y: mouse_cur_pos.y - mouse_start_pos[viewPrefix].y,
      }

      move_lines(viewPrefix, lines, handle_delta, direction)
    }
  }
}

function install_direction_handler(linename, lines, viewPrefix, rect) {
  var handle = document.getElementById(viewPrefix + '-' + linename + '-handle')
  var line = document.getElementById(viewPrefix + '-' + linename)
  var svg = document.getElementById(viewPrefix + '-' + 'view-svg')

  handle.onmouseenter = function () {
    line.style.stroke = 'yellow'
  }

  handle.onmouseleave = hide

  handle.ondblclick = function () {
    onDirectionChanged(Math.PI, null, viewPrefix)
  }

  function hide() {
    line.style.stroke = '#00000000'
  }

  handle.onmouseup = function () {
    line.style.stroke = '#00000000'
    handle.onmouseleave = hide
  }

  handle.onmousedown = function (event) {
    line.style.stroke = 'yellow'
    handle.onmouseleave = null
    highlight_lines(lines)
    disable_handle_except(handle, viewPrefix)

    var handle_center = {
      x: parseInt(line.getAttribute('x1')),
    }

    mouse_start_pos[viewPrefix] = {
      x: event.clientX,
      y: event.clientY - rect.top,

      handle_offset_x: handle_center.x - event.clientX,
    }

    var mouse_cur_pos = { x: mouse_start_pos[viewPrefix].x, y: mouse_start_pos[viewPrefix].y }

    var theta = 0

    svg.onmousemove = function (event) {
      mouse_cur_pos = { x: event.clientX, y: event.clientY - rect.top }

      var handle_center_cur_pos = {
        x: mouse_cur_pos.x + mouse_start_pos[viewPrefix].handle_offset_x,
        y: mouse_cur_pos.y - view_port_pos[viewPrefix].y,
      }

      theta = Math.atan2(
        handle_center_cur_pos.y - view_center[viewPrefix].y,
        handle_center_cur_pos.x - view_center[viewPrefix].x
      )

      rotate_lines(theta, lines, viewPrefix)
    }

    svg.onmouseup = function (event) {
      svg.onmousemove = null
      svg.onmouseup = null

      line.style.stroke = '#00000000'
      enable_handles(viewPrefix)
      handle.onmouseleave = hide

      if (theta == 0) {
        return
      }

      onDirectionChanged(-theta - Math.PI / 2, event.ctrlKey, viewPrefix)
    }
  }
}

function getLines(viewPrefix) {
  return {
    top: document.getElementById(viewPrefix + 'line-top'),
    bottom: document.getElementById(viewPrefix + 'line-bottom'),
    left: document.getElementById(viewPrefix + 'line-left'),
    right: document.getElementById(viewPrefix + 'line-right'),
    direction: document.getElementById(viewPrefix + 'line-direction'),
  }
}

function getHandles(viewPrefix) {
  return {
    top: document.getElementById(viewPrefix + 'line-top-handle'),
    bottom: document.getElementById(viewPrefix + 'line-bottom-handle'),
    left: document.getElementById(viewPrefix + 'line-left-handle'),
    right: document.getElementById(viewPrefix + 'line-right-handle'),
    direction: document.getElementById(viewPrefix + 'line-direction-handle'),

    topleft: document.getElementById(viewPrefix + 'top-left-handle'),
    topright: document.getElementById(viewPrefix + 'top-right-handle'),
    bottomleft: document.getElementById(viewPrefix + 'bottom-left-handle'),
    bottomright: document.getElementById(viewPrefix + 'bottom-right-handle'),

    move: document.getElementById(viewPrefix + 'move-handle'),
  }
}

function highlight_lines(lines) {
  for (var l in lines) {
    lines[l].style.stroke = 'yellow'
  }
}

function hide_lines(lines) {
  for (var l in lines) {
    lines[l].style.stroke = '#00000000'
  }
}

function enable_handles(viewPrefix) {
  const handles = getHandles(viewPrefix + '-')
  for (var h in handles) {
    handles[h].style.display = 'inherit'
  }
}

function move_lines(viewPrefix, lines, delta, direction) {
  var x1 = view_center[viewPrefix].x - view_handle_dimension[viewPrefix].x / 2
  var y1 = view_center[viewPrefix].y - view_handle_dimension[viewPrefix].y / 2
  var x2 = view_center[viewPrefix].x + view_handle_dimension[viewPrefix].x / 2
  var y2 = view_center[viewPrefix].y + view_handle_dimension[viewPrefix].y / 2

  if (direction) {
    if (direction.x == 1) {
      //right
      x2 += delta.x
    } else if (direction.x == -1) {
      //left
      x1 += delta.x
    }

    if (direction.y == -1) {
      //bottom
      y2 += delta.y
    } else if (direction.y == 1) {
      //top
      y1 += delta.y
    }
  } else {
    x1 += delta.x
    y1 += delta.y
    x2 += delta.x
    y2 += delta.y
  }

  set_line_pos(lines, Math.ceil(x1), Math.ceil(x2), Math.ceil(y1), Math.ceil(y2))
}

function set_line_pos(lines, x1, x2, y1, y2) {
  lines.top.setAttribute('x1', '0%')
  lines.top.setAttribute('y1', y1)
  lines.top.setAttribute('x2', '100%')
  lines.top.setAttribute('y2', y1)

  lines.bottom.setAttribute('x1', '0%')
  lines.bottom.setAttribute('y1', y2)
  lines.bottom.setAttribute('x2', '100%')
  lines.bottom.setAttribute('y2', y2)

  lines.left.setAttribute('x1', x1)
  lines.left.setAttribute('y1', '0%')
  lines.left.setAttribute('x2', x1)
  lines.left.setAttribute('y2', '100%')

  lines.right.setAttribute('x1', x2)
  lines.right.setAttribute('y1', '0%')
  lines.right.setAttribute('x2', x2)
  lines.right.setAttribute('y2', '100%')
}

// when direction handler is draging
function rotate_lines(theta, lines, viewPrefix) {
  theta = -theta - Math.PI / 2

  // we use rotation matrix
  var trans_matrix = [
    Math.cos(theta),
    Math.sin(theta),
    view_center[viewPrefix].x,
    -Math.sin(theta),
    Math.cos(theta),
    view_center[viewPrefix].y,
    0,
    0,
    1,
  ]

  var points
  var trans_points

  points = [0, -view_center[viewPrefix].y, 1]
  trans_points = matmul2(trans_matrix, points, 3)
  lines.direction.setAttribute('x2', Math.ceil(trans_points[0]))
  lines.direction.setAttribute('y2', Math.ceil(trans_points[1]))

  points = [
    -view_center[viewPrefix].x,
    view_center[viewPrefix].x,
    -view_handle_dimension[viewPrefix].y / 2,
    -view_handle_dimension[viewPrefix].y / 2,
    1,
    1,
  ]
  var trans_points = matmul2(trans_matrix, points, 3)

  lines.top.setAttribute('x1', Math.ceil(trans_points[0]))
  lines.top.setAttribute('y1', Math.ceil(trans_points[0 + 2]))
  lines.top.setAttribute('x2', Math.ceil(trans_points[1]))
  lines.top.setAttribute('y2', Math.ceil(trans_points[1 + 2]))

  points = [
    -view_handle_dimension[viewPrefix].x / 2,
    -view_handle_dimension[viewPrefix].x / 2,
    -view_center[viewPrefix].y,
    view_center[viewPrefix].y,
    1,
    1,
  ]
  trans_points = matmul2(trans_matrix, points, 3)

  lines.left.setAttribute('x1', Math.ceil(trans_points[0]))
  lines.left.setAttribute('y1', Math.ceil(trans_points[0 + 2]))
  lines.left.setAttribute('x2', Math.ceil(trans_points[1]))
  lines.left.setAttribute('y2', Math.ceil(trans_points[1 + 2]))

  points = [
    view_center[viewPrefix].x,
    -view_center[viewPrefix].x,
    view_handle_dimension[viewPrefix].y / 2,
    view_handle_dimension[viewPrefix].y / 2,
    1,
    1,
  ]
  trans_points = matmul2(trans_matrix, points, 3)
  lines.bottom.setAttribute('x1', Math.ceil(trans_points[1]))
  lines.bottom.setAttribute('y1', Math.ceil(trans_points[1 + 2]))
  lines.bottom.setAttribute('x2', Math.ceil(trans_points[0]))
  lines.bottom.setAttribute('y2', Math.ceil(trans_points[0 + 2]))

  points = [
    view_handle_dimension[viewPrefix].x / 2,
    view_handle_dimension[viewPrefix].x / 2,
    -view_center[viewPrefix].y,
    view_center[viewPrefix].y,
    1,
    1,
  ]
  trans_points = matmul2(trans_matrix, points, 3)

  lines.right.setAttribute('x1', Math.ceil(trans_points[0]))
  lines.right.setAttribute('y1', Math.ceil(trans_points[0 + 2]))
  lines.right.setAttribute('x2', Math.ceil(trans_points[1]))
  lines.right.setAttribute('y2', Math.ceil(trans_points[1 + 2]))
}

function update_view_handle(viewPrefix, viewport, obj_dimension, on_direction_changed = null) {
  const viewport_ratio = viewport.width / viewport.height

  var box_ratio = obj_dimension.x / obj_dimension.y

  view_port_pos[viewPrefix].x = viewport.left
  view_port_pos[viewPrefix].y = viewport.bottom - viewport.height

  var width = 0
  var height = 0

  if (box_ratio > viewport_ratio) {
    // y view
    width = (viewport.width * (2 / 3)) / viewport.zoom_ratio
    height = (width * 0.9) / box_ratio
  } else {
    // z and x view
    height = (viewport.height * 2) / 3 / viewport.zoom_ratio
    width = (height * box_ratio) / 0.85
  }

  view_handle_dimension[viewPrefix].x = width
  view_handle_dimension[viewPrefix].y = height

  var x = viewport.width / 2
  var y = viewport.height / 2

  var left = x - width / 2
  var right = x + width / 2
  var top = y - height / 2
  var bottom = y + height / 2

  view_center[viewPrefix].x = x
  view_center[viewPrefix].y = y

  const lines = getLines(viewPrefix + '-')
  set_line_pos(lines, left, right, top, bottom)

  // note when the object is too thin, the height/width value may be negative,
  // this causes error reporting, but we just let it be.

  const handles = getHandles(viewPrefix + '-')
  var de = handles.left
  de.setAttribute('x', Math.ceil(left - 10))
  de.setAttribute('y', '0%')
  de.setAttribute('height', '100%')
  de.setAttribute('width', 20)

  de = handles.right
  de.setAttribute('x', Math.ceil(right - 10))
  de.setAttribute('y', '0%')
  de.setAttribute('height', '100%')
  de.setAttribute('width', 20)

  de = handles.top
  de.setAttribute('x', '0%')
  de.setAttribute('y', Math.ceil(top - 10))
  de.setAttribute('width', '100%')
  de.setAttribute('height', 20)

  de = handles.bottom
  de.setAttribute('x', '0%')
  de.setAttribute('y', Math.ceil(bottom - 10))
  de.setAttribute('width', '100%')
  de.setAttribute('height', 20)

  de = handles.topleft
  de.setAttribute('x', Math.ceil(left - 10))
  de.setAttribute('y', Math.ceil(top - 10))

  de = handles.topright
  de.setAttribute('x', Math.ceil(right - 10))
  de.setAttribute('y', Math.ceil(top - 10))

  de = handles.bottomleft
  de.setAttribute('x', Math.ceil(left - 10))
  de.setAttribute('y', Math.ceil(bottom - 10))

  de = handles.bottomright
  de.setAttribute('x', Math.ceil(right - 10))
  de.setAttribute('y', Math.ceil(bottom - 10))

  //direction
  if (on_direction_changed) {
    de = lines.direction
    de.setAttribute('x1', Math.ceil((left + right) / 2))
    de.setAttribute('y1', Math.ceil((top + bottom) / 2))
    de.setAttribute('x2', Math.ceil((left + right) / 2))
    de.setAttribute('y2', Math.ceil(0))

    de = handles.direction
    de.setAttribute('x', Math.ceil((left + right) / 2 - 10))
    de.setAttribute('y', 0) //Math.ceil(top+10));
    de.setAttribute('height', Math.ceil((bottom - top) / 2 - 10 + top))
  } else {
    de = lines.direction
    de.style.display = 'none'

    de = handles.direction
    de.style.display = 'none'
  }

  // move handle
  de = document.getElementById(viewPrefix + '-move-handle')
  de.setAttribute('x', Math.ceil((left + right) / 2 - 20))
  de.setAttribute('y', Math.ceil((top + bottom) / 2 - 20))
}

function onDirectionChanged(theta, sticky, viewPrefix) {
  selected_box.onDirectionChanged(theta, sticky, viewPrefix)
}

function disable_handle_except(exclude, viewPrefix) {
  const handles = getHandles(viewPrefix + '-')
  for (var h in handles) {
    if (handles[h] != exclude) handles[h].style.display = 'none'
  }
}

function on_edge_changed_axis(ratio, direction, viewPrefix) {
  let delta
  if (viewPrefix === 'z') {
    delta = {
      x: selected_box.scale.x * ratio.y * direction.y,
      y: selected_box.scale.y * ratio.x * direction.x,
      z: 0,
    }

    direction = {
      x: direction.y,
      y: -direction.x,
      z: 0,
    }
  } else if (viewPrefix === 'y') {
    delta = {
      x: selected_box.scale.x * ratio.x * direction.x,
      z: selected_box.scale.z * ratio.y * direction.y,
      y: 0,
    }

    direction = {
      x: direction.x,
      z: direction.y,
      y: 0,
    }
  } else if (viewPrefix === 'x') {
    delta = {
      y: selected_box.scale.y * ratio.x * direction.x,
      z: selected_box.scale.z * ratio.y * direction.y,
      x: 0,
    }

    direction = {
      y: direction.x,
      z: direction.y,
      x: 0,
    }
  }

  on_edge_changed(delta, direction)
}

function on_edge_changed(delta, direction) {
  selected_box.translate_box('x', (delta.x / 2) * direction.x)
  selected_box.translate_box('y', (delta.y / 2) * direction.y)
  selected_box.translate_box('z', (delta.z / 2) * direction.z)

  selected_box.scale_box('x', delta)
  selected_box.scale_box('y', delta)
  selected_box.scale_box('z', delta)

  selected_box.focusInSideViews()
}

function on_moved_axis(viewPrefix, ratio) {
  if (viewPrefix === 'z') {
    const delta = {
      x: selected_box.scale.x * ratio.y,
      y: -selected_box.scale.y * ratio.x,
    }

    selected_box.translate_box('x', delta.x)
    selected_box.translate_box('y', delta.y)
  } else if (viewPrefix === 'y') {
    const delta = {
      x: selected_box.scale.x * ratio.x,
      z: selected_box.scale.z * ratio.y,
    }

    selected_box.translate_box('x', delta.x)
    selected_box.translate_box('z', delta.z)
  } else if (viewPrefix === 'x') {
    const delta = {
      y: selected_box.scale.y * ratio.x,
      z: selected_box.scale.z * ratio.y,
    }

    selected_box.translate_box('y', delta.y)
    selected_box.translate_box('z', delta.z)
  }

  selected_box.focusInSideViews()
}

export { views, SideViews }
